package com.lagou.edu.ioc;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Kirito
 * @description: 从beans.xml中读取bean信息，并通过反射实力化bean对象，放到缓存中，供外部使用
 * @date 2020/04/04
 */
public class BeanFactory {

    private static Map<String, Object> containers = new ConcurrentHashMap<>();

    private BeanFactory() {

    }

    static {

        try {
            InputStream resource = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
            Document document = new SAXReader().read(resource);
            Element rootElement = document.getRootElement();
            // 解析bean
            List<Element> elements = rootElement.selectNodes("//bean");
            for (Element element : elements) {
                String id = element.attributeValue("id");
                String clazz = element.attributeValue("class");
                Object instance = Class.forName(clazz).newInstance();
                containers.put(id, instance);
            }

            // 解析property之间的依赖关系
            List<Element> properties = rootElement.selectNodes("//property");
            for (Element property : properties) {
                // 获取property元素归属bean元素中对应的属性注入方法，然后进行属性注入
                // eg: <bean id="transactionManager"> <property name="connectionUtils", ref="connectionUtils"/> </bean>

                // transactionManager
                String parentId = property.getParent().attributeValue("id");
                // TransactionManager
                Object parent = containers.get(parentId);

                // <property name="connectionUtils" />
                String name = property.attributeValue("name");
                String referenceId = property.attributeValue("ref");

                Method[] methods = parent.getClass().getDeclaredMethods();
                for (Method method : methods) {
                    if (method.getName().equalsIgnoreCase("set" + name)) {
                        method.invoke(parent, containers.get(referenceId));
                    }
                }
                // 将执行注入后的bean重新添加到缓存中
                containers.put(parentId, parent);
            }
        } catch (DocumentException | ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }

    public static Object getObject(String id) {
        return containers.get(id);
    }

}
